package com.dliyun.oap.framework.impl;

import com.dliyun.oap.framework.SystemParameterNames;
import com.dliyun.oap.framework.annotation.HttpAction;
import com.dliyun.oap.framework.context.OapContext;
import com.dliyun.oap.framework.context.OapRequestContext;
import com.dliyun.oap.framework.response.OapResponse;
import com.dliyun.oap.framework.security.SecurityManager;
import com.dliyun.oap.framework.security.*;
import com.dliyun.oap.framework.service.ServiceMethodHandler;
import com.dliyun.oap.framework.utils.UploadFileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author stjdydayou
 */
@Slf4j
public class DefaultSecurityManager implements SecurityManager {

    @Autowired
    private InvokeTimesController invokeTimesController;

    @Autowired
    private ServiceAccessController serviceAccessController;

    @Autowired
    private AppSecretManager appSecretManager;

    @Autowired
    private FileUploadController fileUploadController;

    @Override
    public OapResponse validateSystemParameters(OapRequestContext orc, OapContext oapContext) {

        // 1.检查method参数
        if (orc.getMethod() == null) {
            return OapResponse.fail("MISSING_METHOD", String.format("服务请求缺少方法名参数:%s，缺少方法", SystemParameterNames.METHOD));
        }
        if (oapContext.isValidMethod(orc.getMethod())) {
            return OapResponse.fail("INVALID_METHOD", "无效的方法，请检查服务方法名是否正确");
        }

        // 2.检查v参数
        if (orc.getVersion() == null) {
            return OapResponse.fail("MISSING_VERSION", String.format("服务方法(%s)缺少版本参数:%s，缺少版本参数", orc.getMethod(), SystemParameterNames.VERSION));
        }
        if (!oapContext.isValidVersion(orc.getMethod(), orc.getVersion())) {
            return OapResponse.fail("UNSUPPORTED_VERSION", String.format("服务方法(%s)不支持对应的版本号(%s)", orc.getMethod(), orc.getVersion()));
        }

        // 4.检查服务方法的版本是否已经过期
        if (orc.getServiceMethodHandler().getServiceMethodDefinition().isObsoleted()) {
            return OapResponse.fail("METHOD_OBSOLETED", String.format("服务方法(%s:%s)已经作废，方法已过时", orc.getMethod(), orc.getVersion()));
        }

        // 7.检查请求HTTP方法的匹配性
        OapResponse oapError = validateHttpAction(orc);
        if (oapError != null) {
            return oapError;
        } else {
            return null;
        }
    }

    @Override
    public OapResponse validateOther(OapRequestContext orc, Map<String, Object> requestParams) {

        // 1.判断应用/用户是否有权访问目标服务
        OapResponse response = checkServiceAccessAllow(orc);
        if (response != null) {
            return response;
        }

        // 2.判断应用/会话/用户访问服务的次数或频度是否超限
        response = checkInvokeTimesLimit(orc);
        if (response != null) {
            return response;
        }

        // 3.如果是上传文件的服务，检查文件类型和大小是否满足要求
        response = checkUploadFile(orc, requestParams);
        if (response != null) {
            return response;
        }

        // 4.检查业务参数合法性
        response = validateBusinessParams(orc);
        if (response != null) {
            return response;
        }
        return null;
    }

    private OapResponse checkUploadFile(OapRequestContext orc, Map<String, Object> requestParams) {
        ServiceMethodHandler serviceMethodHandler = orc.getServiceMethodHandler();
        if (serviceMethodHandler != null && serviceMethodHandler.hasUploadFiles()) {
            List<String> fileFieldNames = serviceMethodHandler.getUploadFileFieldNames();
            for (String fileFieldName : fileFieldNames) {
                String paramValue = requestParams.getOrDefault(fileFieldName, "").toString();
                if (StringUtils.isNotBlank(paramValue)) {
                    String fileType = UploadFileUtil.getFileType(paramValue);
                    if (!fileUploadController.isAllowFileType(fileType)) {
                        return OapResponse.fail("FILE_TYPE_NOT_ALLOW", "the valid file types is" + fileUploadController.getAllowFileTypes());
                    }
                    byte[] fileContent = UploadFileUtil.decode(paramValue);
                    if (fileUploadController.isExceedMaxSize(fileContent.length)) {
                        return OapResponse.fail("EXCEED_MAX_SIZE", "EXCEED_MAX_SIZE:" + fileUploadController.getMaxSize() + "k");
                    }
                }
            }
        }
        return null;
    }

    private OapResponse checkInvokeTimesLimit(OapRequestContext rrctx) {
        if (invokeTimesController.isAppInvokeFrequencyExceed(rrctx.getAppKey())) {
            return OapResponse.fail("EXCEED_APP_INVOKE_FREQUENCY_LIMITED", "应用调用服务的频率超限");
        } else if (invokeTimesController.isAppInvokeLimitExceed(rrctx.getAppKey())) {
            return OapResponse.fail("EXCEED_APP_INVOKE_LIMITED", "应用调用服务的次数超限");
        } else {
            return null;
        }
    }

    /**
     * 校验是否是合法的HTTP动作
     *
     * @param orc
     */
    private OapResponse validateHttpAction(OapRequestContext orc) {
        OapResponse response = null;
        HttpAction[] httpActions = orc.getServiceMethodHandler().getServiceMethodDefinition().getHttpAction();
        if (httpActions.length > 0) {
            boolean isValid = false;
            for (HttpAction httpAction : httpActions) {
                if (httpAction == orc.getHttpAction()) {
                    isValid = true;
                    break;
                }
            }
            if (!isValid) {
                response = OapResponse.fail("HTTP_ACTION_NOT_ALLOWED", String.format("服务方法(%s:%s)HTTP方法%s被禁止", orc.getMethod(), orc.getVersion(), orc.getHttpAction()));
            }
        }
        return response;
    }

    private OapResponse checkServiceAccessAllow(OapRequestContext orc) {
        if (!this.serviceAccessController.isAppGranted(orc.getAppKey(), orc.getMethod(), orc.getVersion())) {
            OapResponse oapError = OapResponse.fail("INVALID_PERMISSION", "权限不够、非法访问");
            if (log.isDebugEnabled()) {
                log.debug("未向ISV开放该服务的执行权限(" + orc.getMethod() + ")");
            }
            return oapError;
        } else {
            return null;
        }
    }

    private OapResponse validateBusinessParams(OapRequestContext orc) {
        List<ObjectError> bindingErrors = orc.getBindingErrors();

        // 将Bean数据绑定时产生的错误转换为Oap的错误
        if (bindingErrors != null && bindingErrors.size() > 0) {
            OapResponse oapResponse = OapResponse.fail("INVALID_PARAMETER", " 参数无效，格式不对、非法值、越界等 ");
            List<String> errors = new ArrayList<>();
            for (ObjectError objectError : bindingErrors) {
                if (objectError instanceof FieldError) {
                    errors.add(((FieldError) objectError).getField() + objectError.getDefaultMessage());
                }
            }
            oapResponse.setBody(String.join(",", errors));
            return oapResponse;
        } else {
            return null;
        }
    }
}
